#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#codeby     道长且阻
#email      ydhcui@suliu.net/QQ664284092
#website    http://www.suliu.net
#

__all__ = [
    'login',
    'send_user_msg',
    'send_group_msg',
    'send_discu_msg',
    'get_uin2qq',
    'get_user_info',
    'allow_add_friend',
    'Queue',
]


import os
import sys
import json
import random
import time
import socket
import queue
from urllib import parse,request
from http import cookiejar

import util
import qqhash
from log import logger
from message import QQMessageManage

#socket.setdefaulttimeout(120)

class QQlogin(object):
    def _request(self, url, data=None,cookie=1,timeout=0):
        if timeout:socket.setdefaulttimeout(timeout)  #设置超时
        if data:
            data = parse.urlencode(data).encode('utf-8')
            rr = request.Request(url, data, self._headers)
        else:
            rr = request.Request(url=url, headers=self._headers)
        try:
            fp=self.opener.open(rr)
        except Exception as e: #socket time out
            return e
        if fp:
            if fp.info().get('Content-Encoding') == 'gzip':
                '''
                py3k
                f = gzip.decompress(fp.read())
                res = f.decode('utf-8')
                -----------------------
                py2k
                try:
                  gf = GzipFile(fileobj=StringIO(fp.read()), mode="r")
                  f = gf.read()
                except:
                  f = gf.extrabuf
                res = f.decode('utf-8')
                '''
                pass
            else:
                res = fp.read()
                try:
                    res = res.decode('utf-8')
                except:
                    res = res
            ct = fp.info().get('Content-Type')
            if ct == 'text/plain; charset=utf-8' or ct == 'application/json;charset=utf-8':
                res = json.loads(res)
                if res['retcode'] == 0: #success
                    res = res['result']
                elif res['retcode'] == 103: #cookie timeout
                    self.login()
                elif res['retcode'] == 102: #ok
                    res = None
                elif res['retcode'] == 114: #send msg fail TODO:retry
                    res = None
                elif res['retcode'] == 116: #update ptwebqq value
                    self.logininfo.update({'ptwebqq':res['p']})
                    res = None
                elif res['retcode'] == 100002: #login with cookie
                    res = None
                else:
                    res = None#self.__del__()
            if cookie:
                self.cookieJar.save(self.COOKIE,ignore_discard=True, ignore_expires=True)
        return res

    def __init__(self, qq, pw):
        self.qq                      = qq
        self._pw                     = pw
        self.msgid                   = random.randrange(1111111, 99999999)
        self.verifycode              = None
        self.logininfo               = {}
        self.logininfo['appid']      = "501004106"
        self.logininfo['login_sig']  = ""
        self.logininfo['verifysession'] = ""
        self.logininfo['r']          = random.Random().random
        self.logininfo['clientid']   = random.randrange(11111111, 99999999)

        self.COOKIE = util.getres("res/%swebqq.so"  %self.qq)
        self.VERIFY = util.getres("res/%swebqq.png" %self.qq)
        self.QRPATH = util.getres("res/%swebqq.png" %self.qq)
        self.IS_VERIFCODE    = False    #是否要验证码
        self.IS_LOGIND       = False    #是否已登录

        self.cookieJar = cookiejar.MozillaCookieJar(self.COOKIE)
        self.opener = request.build_opener(request.HTTPCookieProcessor(self.cookieJar))
        self._headers = {
            "User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20111111 Firefox/14.0.1",
            "Accept-Language":"zh-cn,en;q=0.8,en-us;q=0.5,zh-hk;q=0.3",
            #"Accept-Encoding":"gzip;deflate",
            "Connection":"keep-alive",
            "Referer":"http://v.qq.com"
        }

    def _getverifycode(self):
        url = "https://ssl.ptlogin2.qq.com/check?pt_tea=1&uin={0}&appid={1}&js_ver=10124&js_type=0&login_sig={2}&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html&r={3}" \
        .format(self.qq,
                self.logininfo['appid'],
                self.logininfo['login_sig'],
                self.logininfo['r']())

        res = self._request(url = url)
        """
        ptui_checkVC('0','!AON','\x00\x00\x00\x00\x44\x87\xe9\xe9','031afa407792824308292ce265fbc80c5c5f44ce10fdf5bc273dfd16bc31edd0579f157f9cfdb16d0d85691df1ad38d1c5560b2a560f2612','0');
        """
        verify =  list(eval(res.split("(")[1].split(")")[0]))
        if verify[0] == '1':
            img = "https://ssl.captcha.qq.com/getimage?aid={}&r={}&uin={}" \
            .format(self.logininfo['appid'],
                    self.logininfo['r'],
                    self.qq)
            self.logininfo.update(dict([(x.name,x.value) for x in self.cookieJar]))
            fp = open(self.VERIFY,"wb")
            fp.write(self._request(img))
            fp.close()
            logger.info(u"验证码请查看res文件夹下的%s.png文件。"%self.qq)
            logger.info(u"请输入验证码：")
            #util.showimg(self.VERIFY)
            verify[1] = input()#self.verifycode.strip().upper()
        elif verify[0] == '0':
            self.logininfo['verifysession'] = verify[3]
        return verify

    def _getloginsig(self):
        import re
        url="https://ui.ptlogin2.qq.com/cgi-bin/login?daid=164&target=self&style=16&mibao_css=m_webqq&appid={0}".format(self.logininfo['appid']) \
           +"&enable_qlogin=0&no_verifyimg=1&s_url=http%3A%2F%2Fw.qq.com%2Fproxy.html&f_url=loginerroralert&strong_login=1&login_state=10&t=2013102400"
        res=self._request(url)
        self.logininfo['loginsig'] = re.compile('g_login_sig=encodeURIComponent\("(.*?)"\);').findall(res)[0]

class QQapi(QQlogin):

    def __init__(self, qq, pw):
        super(QQapi, self).__init__(qq, pw)
        self.sendtime = time.time()
        self.sendmsg = None
        self.friends = {}#好友列表
        self.groups  = {}#群组列表
        self.discus  = {}#讨论组列表

    def QRlogin(self):
        qrimg = "https://ssl.ptlogin2.qq.com/ptqrshow?appid=501004106&e=0&l=M&s=5&d=72&v=4&t=0.5431659338064492"
        qrscan = "https://ssl.ptlogin2.qq.com/ptqrlogin?webqq_type=10&remember_uin=1&login2qq=1&aid=501004106&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=0-0-4275&mibao_css=m_webqq&t=1&g=1&js_type=0&js_ver=10124&login_sig=&pt_randsalt=0"
        """ptuiCB('66','0','','0','二维码未失效。(3891295104)', '10000');"""


    def login(self):

        logger.info('login...%s'%self.qq)
        if os.path.isfile(self.COOKIE):
            self.cookieJar.load(self.COOKIE,ignore_discard=True, ignore_expires=True)
            logger.info('login from cookies...%s'%self.qq)
        else:
            self._getloginsig()
            self._verifycode = self._getverifycode()
            self.pswd = qqhash.getqqpass(self.qq,self._pw,self._verifycode[1])
            #logger.debug(self.pswd)
            url = "https://ssl.ptlogin2.qq.com/login?u={0}&p={1}&verifycode={2}&webqq_type=10&remember_uin=1&login2qq=1&aid={3}&u1=http%3A%2F%2Fw.qq.com%2Fproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=0-24-46708&mibao_css=m_webqq&t=2&g=1&js_type=0&js_ver=10124&login_sig=&pt_randsalt=0&pt_vcode_v1=0&pt_verifysession_v1={4}" \
            .format(self.qq,
                    self.pswd,
                    self._verifycode[1],
                    self.logininfo['appid'],
                    self.logininfo['verifysession'])

            res = self._request(url=url)
            logger.info(str(res))
            ptuiCB= eval(res.split("ptuiCB(")[1].split(");")[0])
            pb1,pb2,pburl,pb3,pbstatus,pbnick = ptuiCB
            if pbstatus.find("成功") != -1:
                self._request(pburl)
            elif pbstatus.find("验证码") != -1:
                logger.info("验证码错误")
                self._getverifycode()
                self.login()
            else:
                logger.info(ptuiCB)
        self.IS_LOGIND = True #已登录
        self.logininfo.update(dict([(x.name,x.value) for x in self.cookieJar]))
        self.get_login_info()
        self.getvfwebqq()
        self.poll()

    def get_login_info(self):
        try:
          url = "http://d.web2.qq.com/channel/login2"
          data = {'r':json.dumps({'status':'online','ptwebqq':self.logininfo['ptwebqq'],'clientid':self.logininfo['clientid']})}
          res = self._request(url, data)
          self.logininfo.update(res)
        except:
          os.remove(self.COOKIE)
          self.login()
          '''{"retcode":0,
              "result":{"uin":1149757929,
                        "cip":1851151858,
                        "index":1075,
                        "port":35925,
                        "status":"online",
                        "vfwebqq":"b9556a8b4f0cbdce9db285a90a34c0c912b36d89badc6b224d16eacd25cd8a0c3857e3bcaefd2f",
                        "psessionid":"8368046764001d63f6e67365727665725f77656271714031302e3133392e372e31363000007f9100000841036e0400e9e987446d0000000a40543677333456357a676d00000028b9556a8b4f0cbdce9db2485a9c0a34c0c912b36d89badc6b224d16eacd25cd8a0c3857e3bcaefd2f",
                        "user_state":0,
                        "f":0}}

          '''
    def getvfwebqq(self):
        url = "http://s.web2.qq.com/api/getvfwebqq?ptwebqq={}&clientid={}&psessionid=&t={}" \
            .format(self.logininfo['ptwebqq'],self.logininfo['clientid'],random.randrange(1345457600000,1345458000000))
        res = self._request(url)
        self.logininfo.update(res)
        """
        {"retcode":0,"result":{"vfwebqq":"2cede292f584fb2912a5c8e7c541eccec0d61c5d0d5880d686d0c6516e5296cb62a327cf2010bbd"}}
        """

    def poll(self):
        """ 开始心跳"""
        k=0
        while True:
            try:
                self._headers.update({"Referer":"http://v.qq.com",})
                url = "http://d.web2.qq.com/channel/poll2"
                status = {'clientid':self.logininfo['clientid'],
                          'psessionid':self.logininfo['psessionid'],
                          'key':0,
                          'ids':[],
                          }
                data = {'r':json.dumps(status),
                        'clientid' : self.logininfo['clientid'],
                        'psessionid':self.logininfo['psessionid'],
                        }
                res = self._request(url,data)
                if res:
                    QQMessageManage.Queue.put(res)
            except Exception as e:
                logger.error(str(e))
                time.sleep(3)
                k += 1
                if k>10:
                    self.login()



    def makemsgid(self):
        self.msgid += 1
        return self.msgid

    def makefont(self,name='宋体',size='9',color='000000'):
      return ["font",{"name":name,"size":size,"style":[0,0,0],"color":color}]

    def makemsg(self,msg,face=None):
        #如果在短时间(3s)内发送的消息相同则剔除 不发送
        if time.time()-self.sendtime <= 3 and self.sendmsg==msg:
           return
        self.sendtime=time.time()
        self.sendmsg =str(msg)
        content=[]
        content.append(msg.replace('\n','\r'))
        if face:
          content.append(["face",face])
          content.append(" ")
        content.append(self.makefont())
        return json.dumps(content)

    def send_user_msg(self,uin,msg,face=None):
        """发送个人消息"""
        if not msg:return
        url = "http://d.web2.qq.com/channel/send_buddy_msg2"
        status = {'to':uin,
            'face':180,
            'content':self.makemsg(msg,face),
            'msg_id':self.makemsgid(),
            'clientid':self.logininfo['clientid'],
            "psessionid":self.logininfo['psessionid']
            }
        data = {'r':json.dumps(status),}
        res = self._request(url, data)
        return res == 'ok'

    def send_group_msg(self,uin,msg,face=None):
        """发送群消息"""
        if not msg:return
        url = "http://d.web2.qq.com/channel/send_qun_msg2"
        status = {"group_uin":uin,
                  "content":self.makemsg(msg,face),
                  "msg_id":self.makemsgid(),
                  "face":540,
                  "clientid":self.logininfo['clientid'],
                  "psessionid":self.logininfo['psessionid'],}
        data = {'r':json.dumps(status),}
        res = self._request(url, data)
        return res == 'ok'

    def send_discu_msg(self,did,msg,face=None):
        """发送讨论组消息"""
        if not msg:return
        url = "http://d.web2.qq.com/channel/send_discu_msg2"
        status = {"did":did,
            "content":self.makemsg(msg,face),
            "msg_id":self.makemsgid(),
            "clientid":self.logininfo['clientid'],
            "psessionid":self.logininfo['psessionid']
            }
        data = {'r':json.dumps(status)}
        res = self._request(url, data)
        return res == 'ok'

    def send_sess_msg(self,to,group_sig,msg,face=None):
        """发送临时消息"""

        if not msg:return
        url = "http://d.web2.qq.com/channel/send_sess_msg2"
        status={"to":to,
                "group_sig":group_sig,
                "face":522,
                "content":self.makemsg(msg,face),
                "msg_id":self.makemsgid(),
                "service_type":0,
                "clientid":self.logininfo['clientid'],
                "psessionid":self.logininfo['psessionid'],
               }
        data = {'r':json.dumps(status),}
        res = self._request(url, data)
        return res=='ok'

    def get_uin2qq(self,uin):
        url = "http://s.web2.qq.com/api/get_friend_uin2?tuin={0}&verifysession=&type=1&code=&vfwebqq={1}&t={2}" \
            .format(uin, self.logininfo['vfwebqq'], random.randrange(1345457600000,1345458000000))
        res = self._request(url)
        try:return res['account']
        except:pass

    def get_online(self):
        url = "http://d.web2.qq.com/channel/get_online_buddies2?clientid=%s&psessionid=%s"%(self.logininfo['clientid'],self.logininfo['psessionid'])
        res = self._request(url)
        return res
        """[{"uin":1998330330,"status":"online","client_type":1},...]
        """

    def get_friends(self):
        self._headers.update({
                'Host':'s.web2.qq.com',
                'Origin':'http://s.web2.qq.com',
                'Referer':'http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1',})
        url = "http://s.web2.qq.com/api/get_user_friends2"
        data= {"r":json.dumps({"vfwebqq":self.logininfo['vfwebqq'],
                               "hash":qqhash.getqqhash(self.qq,self.logininfo['ptwebqq']),})}
        res = self._request(url,data)
        return res
        """{"friends":[{"flag":0,"uin":2228117076,"categories":0},],
            "marknames":[{"uin":2301788619,"markname":"罗家君","type":0},],
            "categories":[{"index":0,"sort":0,"name":"我的好友"},
                          {"index":1,"sort":1,"name":"未命名"}],
            "vipinfo":[{"vip_level":0,"u":2228117076,"is_vip":0},],
            "info":[{"face":147,"flag":8913410,"nick":"  oBenz","uin":2228117076},]
            }
        """

    def get_group_name_list(self):
        self._headers.update({
                'Host':'s.web2.qq.com',
                'Origin':'http://s.web2.qq.com',
                'Referer':'http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1',})
        url  = "http://s.web2.qq.com/api/get_group_name_list_mask2"
        data= {"r":json.dumps({"vfwebqq":self.logininfo['vfwebqq'],
                               "hash":qqhash.getqqhash(self.qq,self.logininfo['ptwebqq']),})}
        res  = self._request(url,data)
        return res

        """{"gmasklist":[],
            "gnamelist":[{"flag":1,"name":"溯流","gid":124222594,"code":2544571247},...],
            "gmarklist":[]
           }
        """

    def get_discus_list(self):
        url  = "http://s.web2.qq.com/api/get_discus_list?clientid={}&psessionid={}&vfwebqq={}&t={}" \
             .format(self.logininfo['clientid'],self.logininfo['psessionid'],self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
        res  = self._request(url)
        return res

        """{"retcode":0,"result":{"dnamelist":[]}}
        """

    def get_user_info(self,uin):
        url = "http://s.web2.qq.com/api/get_friend_info2?tuin=%s&vfwebqq=%s&clientid=%s&psessionid=%s&t=%s" \
              %(uin,self.logininfo['vfwebqq'],self.logininfo['clientid'],self.logininfo['psessionid'],random.randrange(1345457600000,1345458000000))
        res = self._request(url)
        return res if res else {}
        """{"face":0,
            "birthday":{"month":8,"year":1998,"day":8},
            "occupation":"",
            "phone":"",
            "allow":0,
            "college":"",
            "uin":1985170374,
            "constel":7,
            "blood":0,
            "homepage":"",
            "stat":10,
            "vip_info":0,
            "country":"",
            "city":"",
            "personal":"输入‘帮助’查看更多功能。\r获取源码请联系 664284092。",
            "nick":" WallE",
            "shengxiao":3,
            "email":"",
            "client_type":41,
            "province":"",
            "gender":"female",
            "mobile":""
            }
        """

    def get_self_info(self):
        url = "http://s.web2.qq.com/api/get_self_info2?t=%s"%random.randrange(1345457600000,1345458000000)
        res= self._request(url)
        return res
        """
        {"retcode":0,
        "result":{"birthday":{"month":1,
                              "year":2009,
                              "day":1},
                  "face":522,
                  "phone":"10086-",
                  "occupation":"",
                  "allow":1,
                  "college":"-",
                  "uin":1173725134,
                  "blood":5,
                  "constel":12,
                  "lnick":"关注网络",
                  "vfwebqq":"631b9c782ec356c3e4d5db41b4f79b99425af0c2ddd58904db9cb568979e52e479d09c03558c8c10",
                  "homepage":"-",
                  "vip_info":0,
                  "city":"通州",
                  "country":"中国",
                  "personal":"",
                  "shengxiao":1,
                  "nick":"WJ-02B",
                  "email":"1173725134@qq.com",
                  "province":"北京",
                  "account":1173725134,
                  "gender":"female",
                  "mobile":""}
        }
        """

    def get_group_info(self,gcode):
        url="http://s.web2.qq.com/api/get_group_info_ext2?gcode=%s&vfwebqq=%s&t=%s" \
            %(gcode,self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
        res= self._request(url)
        return res

        """{stats:[{client_type:1, uin:1945326622, stat:10}, {client_type:1, uin:2995370625, stat:10},…],…}
            cards: [{muin:698150405, card:流溯}, {muin:2164645040, card:兜兜}, {muin:1945326622, card:流溯},…]
            ginfo: {face:0, memo:欢迎各位到来，一起加入我们约会吧。愿你下一伴到来，祝你们幸福, class:10034, fingermemo:, code:1417043986,…}
            minfo: [{nick: vìГTЦ, province:, gender:unknown, uin:2635661014, country:, city:},…]
            vipinfo: [{vip_level:0, u:2047269867, is_vip:0}, ]
        """


    def get_group_sig(self,gcode,to_uin):
        url="http://d.web2.qq.com/channel/get_c2cmsg_sig2?id={0}&to_uin={1}&clientid={2}&psessionid={3}&service_type=0&t={4}" \
            .format(gcode,to_uin,self.logininfo['clientid'],self.logininfo['psessionid'],random.randrange(1345457600000,1345458000000))
        res=self._request(url)
        return res
        """{"type":0,
            "value":"b5bb05faaef1e7106b8d17b4466322ab2ae69b25395e10910b4320ed331794e885dd69a4163180adbd85746151f9efaa",
            "flags":{"text":1,"pic":1,"file":1,"audio":1,"video":1}
            }
        """

    def allow_add_friend(self,uin):
        """加好友"""
        url="http://s.web2.qq.com/api/allow_added_request2"
        data={'r':json.dumps({"account":uin,"vfwebqq":self.logininfo['vfwebqq']})}
        res=self._request(url)
        return res
        """{result:0, client_type:41, account:1149757929, tuin:2428510851, stat:10}"""

    def shake(self,uin):
        """抖窗口"""
        url = "http://d.web2.qq.com/channel/shake2?to_uin=%s&clientid=%s&psessionid=%s&t=%s" \
            %(uin,self.logininfo['clientid'],self.logininfo['psessionid'],random.randrange(1345457600000,1345458000000))
        res=self._request(url)
        return res

    def get_group_pic(self,gid,uin,rip,port,fid,pic):
        url = "http://web2.qq.com/cgi-bin/get_group_pic?type=0&gid={0}&uin={1}&rip={2}&rport={3}&fid={4}&pic={5}&vfwebqq={6}&t={7}" \
            .format(gid,uin,rip,port,fid,pic,self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))

    def get_offpic(self,filepath):
        url = "http://d.web2.qq.com/channel/get_offpic2?file_path=%2F{0}&f_uin={1}&clientid={2}&psessionid={3}"

    def get_single_long_nick(self,uin):
      url = "http://s.web2.qq.com/api/get_single_long_nick2?tuin=1381956409&vfwebqq=%s&t=%s" \
          %(self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
      res = self._request(url)
      return json.loads(res)['lnick']
      '''{"retcode":0,"result":[{"uin":1381956409,"lnick":"   谁没受过伤？ 伤呀伤的就习惯了"}]}
      '''
    def get_qq_level(self,uin):
      url = "http://s.web2.qq.com/api/get_qq_level2?tuin=%s&vfwebqq=%s&t=%s" \
          %(uin,self.logininfo['vfwebqq'],random.randrange(1345457600000,1345458000000))
      res = self._request(url)
      return json.loads(res)
      '''{"retcode":0,"result":{"level":59,"days":3788,"hours":18668,"remainDays":52,"tuin":1381956409}}
      '''

if __name__=="__main__":
  import threading
  Q=QQapi(1149757929,'qlqlqlql')
  def main():
      for data in QQMessageManage.Queue.get():
          logger.info(str(data))
  threading.Thread(target=Q.login).start()
  threading.Thread(target=main).start()

